package laf.agents;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.smartcardio.ATR;

import laf.env.Attribute;
import laf.env.AttributeIdentifier;
import laf.env.AttributeValue;
import laf.env.Enviroment;
import laf.lang.Symbol;

public class LanguageSymbolDictionary implements Serializable {

	private static final long serialVersionUID = -2652627264534339487L;
	TreeMap<Symbol, TreeMap<AttributeValue, Correlation>> map;
	private Collection<Attribute> attributes;
	private long maxCredibility = 0L;
	
	private Map<Symbol, Attribute> attrCorrelations = new HashMap<Symbol, Attribute>();

	public LanguageSymbolDictionary(Collection<Attribute> attributes) {
		this.attributes = new HashSet<Attribute>(attributes);
		map = new TreeMap<Symbol, TreeMap<AttributeValue, Correlation>>();
	}

	public LanguageSymbolDictionary(Collection<Symbol> symbols, Collection<Attribute> attributes) {
		this(attributes);
		for (Symbol symbol : symbols) {
			addSymbol(symbol);
		}
	}

	private void addSymbol(Symbol symbol) {
		TreeMap<AttributeValue, Correlation> innerMap = new TreeMap<AttributeValue, Correlation>();
		map.put(symbol, innerMap);
		for (Attribute attribute : attributes) {
			for (AttributeValue attrValue : attribute.getValues()) {
				innerMap.put(attrValue, new Correlation());
			}
		}
	}

	public LanguageSymbolDictionary(LanguageSymbolDictionary copy) {
		map = new TreeMap<Symbol, TreeMap<AttributeValue, Correlation>>();
		TreeMap<AttributeValue, Correlation> innerMap;
		TreeMap<AttributeValue, Correlation> innerCopyMap;
		Correlation correlation;
		for (Symbol symbol : copy.map.keySet()) {
			innerMap = new TreeMap<AttributeValue, Correlation>();
			map.put(symbol, innerMap);
			innerCopyMap = copy.map.get(symbol);
			for (AttributeValue attrValue : innerCopyMap.keySet()) {
				correlation = new Correlation();
				correlation.correlation = innerCopyMap.get(attrValue).correlation;
				correlation.credibility = innerCopyMap.get(attrValue).credibility;
				innerMap.put(attrValue, correlation);
			}
		}
	}

	public void setCorrelation(Symbol symbol, AttributeValue attrValue, double correlation) {
		if (!map.containsKey(symbol)) {
			addSymbol(symbol);
		}
		map.get(symbol).get(attrValue).correlation = correlation;
	}

	public double getCorrelation(Symbol symbol, AttributeValue attrValue) {
		if (!map.containsKey(symbol)) {
			addSymbol(symbol);
		}
		return map.get(symbol).get(attrValue).correlation;
	}

	public void setCredibility(Symbol symbol, AttributeValue attrValue, long credibility) {
		if (!map.containsKey(symbol)) {
			addSymbol(symbol);
		}
		map.get(symbol).get(attrValue).credibility = credibility;
		if (credibility > maxCredibility) {
			maxCredibility = credibility;
		}
	}

	public long getCredibility(Symbol symbol, AttributeValue attrValue) {
		if (!map.containsKey(symbol)) {
			addSymbol(symbol);
		}
		return map.get(symbol).get(attrValue).credibility;
	}
	
	public long getMaxCredibility() {
		return maxCredibility;
	}

	public String toString() {
		StringBuilder out = new StringBuilder();
		for (Symbol symbol : map.keySet()) {
			out.append(symbol);
			out.append(" || ");
			Map<AttributeValue, Correlation> innerMap = map.get(symbol);
			for (AttributeValue attrValue : innerMap.keySet()) {
				out.append(innerMap.get(attrValue));
				out.append(" | ");
			}
			out.append("\n");
		}
		return out.toString();
	}

	public Collection<Symbol> findCorrelatedSymbols(Collection<AttributeValue> attrValues, double correlationLevel, long credibilityLevel) {
		List<Symbol> symbols = new ArrayList<Symbol>();
		TreeMap<AttributeValue, Correlation> innerMap;
		Correlation correlation;
		for (Symbol symbol : map.keySet()) {
			innerMap = map.get(symbol);
			for (AttributeValue attrValue : innerMap.keySet()) {
				if (attrValues.contains(attrValue)) {
					correlation = innerMap.get(attrValue);
					if (correlation.correlation >= correlationLevel && correlation.credibility >= credibilityLevel) {
						symbols.add(symbol);
					}
				}
			}
		}
		return symbols;
	}

	public Collection<Symbol> getSymbols() {
		return map.keySet();
	}

	public double getEntropy(Symbol symbol, Attribute attr) {
		double entropy = 0.0;
		double correlation;
		double norm = 0.0;
		
		for (AttributeValue attrVal : attr.getValues()) {
			norm += getCorrelation(symbol, attrVal);
		}
		
		for (AttributeValue attrVal : attr.getValues()) {
			correlation = getCorrelation(symbol, attrVal);
			if (norm != 0.0 && correlation != 0.0) {
				correlation /= norm;	// normalize
				entropy -= correlation * Math.log(correlation);
			}
		}
		return entropy;
	}
	
	public Attribute getCorrelatedAttribute(Symbol symbol) {
		return attrCorrelations.get(symbol);
	}
	
	public void setCorrelatedAttribute(Symbol symbol, Attribute attr) {
		attrCorrelations.put(symbol, attr);
	}

	public static void createCorrelation(LanguageSymbolDictionary dict, Symbol symbol, AttributeValue minVal, AttributeValue maxVal, double correlation, long credibility) {
		Enviroment env = Enviroment.getInstance();
		Collection<AttributeValue> values = env.getAttribute(minVal.getAttributeIdentifier()).getValues();
		for (AttributeValue attrVal : values) {
			if (attrVal.compareTo(minVal) >= 0 && attrVal.compareTo(maxVal) <= 0) {
				dict.setCorrelation(symbol, attrVal, correlation);
				dict.setCredibility(symbol, attrVal, credibility);
			}
		}
	}
}
